
package com.jf.cloud.supplier.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import com.jf.cloud.api.leaf.feign.SegmentFeignClient;
import com.jf.cloud.api.multishop.bo.OrderChangeShopWalletAmountBO;
import com.jf.cloud.api.supplier.bo.SupplierWalletBO;
import com.jf.cloud.common.cache.constant.LockCacheNames;
import com.jf.cloud.common.constant.DistributedIdKey;
import com.jf.cloud.common.database.dto.PageDTO;
import com.jf.cloud.common.database.util.PageUtil;
import com.jf.cloud.common.database.vo.PageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.order.vo.OrderSimpleAmountInfoBO;
import com.jf.cloud.common.response.ResponseEnum;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.rocketmq.config.RocketMqConstant;
import com.jf.cloud.supplier.bo.mongo.MongoSupplierWalletLogBO;
import com.jf.cloud.supplier.constant.SupplierWalletAmountType;
import com.jf.cloud.supplier.constant.SupplierWalletChangeReason;
import com.jf.cloud.supplier.constant.SupplierWalletZoneConstant;
import com.jf.cloud.supplier.dto.SupplierWalletLogSearchDTO;
import com.jf.cloud.supplier.mapper.SupplierWalletMapper;
import com.jf.cloud.supplier.mapper.SupplierWalletZoneMapper;
import com.jf.cloud.supplier.model.SupplierWallet;
import com.jf.cloud.supplier.service.SupplierWalletLogService;
import com.jf.cloud.supplier.service.SupplierWalletService;
import com.jf.cloud.supplier.service.SupplierWalletZoneService;
import com.jf.cloud.supplier.vo.SupplierWalletVO;
import com.jf.cloud.supplier.vo.SupplierWalletZoneInfoVO;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 商家钱包信息
 *
 * @author zz
 * @date 2021-01-07 16:41:03
 */
@Service
public class SupplierWalletServiceImpl implements SupplierWalletService {

    @Autowired
    private SupplierWalletMapper supplierWalletMapper;
    @Autowired
    private SupplierWalletLogService supplierWalletLogService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private SegmentFeignClient segmentFeignClient;
    @Autowired
    private RocketMQTemplate addSupplierWalletLogTemplate;
    @Autowired
    private SupplierWalletZoneService supplierWalletZoneService;
    @Autowired
    private SupplierWalletZoneMapper supplierWalletZoneMapper;
    @Autowired
    private RedissonClient redissonClient;

    private static final Logger logger = LoggerFactory.getLogger(SupplierWalletServiceImpl.class);

    @Override
    public PageVO<SupplierWallet> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> supplierWalletMapper.list());
    }

    @Override
    public SupplierWallet getBySupplierWalletId(Long supplierWalletId) {
        return supplierWalletMapper.getBySupplierWalletId(supplierWalletId);
    }

    @Override
    public void save(SupplierWallet supplierWallet) {
        supplierWalletMapper.save(supplierWallet);
    }

    @Override
    public void update(SupplierWallet supplierWallet) {
        supplierWalletMapper.update(supplierWallet);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountByOrder(List<OrderSimpleAmountInfoBO> orderSimpleAmountInfos) {

        // 进行分账，添加一个分账记录表，因为每一个订单进行分完帐后要进行退款，那么退款的时候也应该按照下单的时候的分账比例进行原路返回
        // 获取当前分账的比例

        List<MongoSupplierWalletLogBO> supplierWalletLogList = new ArrayList<>();
        for (OrderSimpleAmountInfoBO orderSimpleAmountInfo : orderSimpleAmountInfos) {
            // 商家之所以会从一笔订单拿到钱，是因为订单支付成功
            long count = supplierWalletLogService.countInAmountByOrderId(orderSimpleAmountInfo.getOrderId(), SupplierWalletChangeReason.PAY.value());
            // 幂等
            if (count > 0) {
                return;
            }
            // 供应商收入金额为 = 采购金额 + 运费 - 平台佣金，在之前已经处理，这里直接拿
            Long changeAmount = orderSimpleAmountInfo.getPurchaseAmount();

            // 商家添加未结算金额(因为添加了唯一索引，所以是不用怕多次加钱)
            Integer zoneNo = addUnsettledAmount(orderSimpleAmountInfo.getSupplierId(), changeAmount, null);

            MongoSupplierWalletLogBO supplierWalletLog = new MongoSupplierWalletLogBO();

            supplierWalletLog.setWalletLogId(orderSimpleAmountInfo.getWalletLogId());
            supplierWalletLog.setOrderId(orderSimpleAmountInfo.getOrderId());
            supplierWalletLog.setReason(SupplierWalletChangeReason.PAY.value());
            supplierWalletLog.setSupplierId(orderSimpleAmountInfo.getSupplierId());
            supplierWalletLog.setZoneNo(zoneNo);
            supplierWalletLog.setIoType(1);
            supplierWalletLog.setCreateTime(new Date());
            supplierWalletLog.setUpdateTime(new Date());
            supplierWalletLog.setChangeAmount(changeAmount);
            supplierWalletLog.setFreightAmount(orderSimpleAmountInfo.getFreightAmount() + Math.abs(orderSimpleAmountInfo.getFreeFreightAmount()));
            supplierWalletLog.setAmountType(SupplierWalletAmountType.UNSETTLED_AMOUNT.value());
            supplierWalletLog.setUserAmount(orderSimpleAmountInfo.getActualTotal());
            supplierWalletLog.setDistributionAmount(orderSimpleAmountInfo.getDistributionAmount());
            supplierWalletLog.setPlatformAmount(orderSimpleAmountInfo.getPlatformAmount());
            supplierWalletLog.setPlatformCommission(orderSimpleAmountInfo.getPurchasePlatformCommission());
            // 添加日志
            logger.info("供应商添加待结算金额完成，增加结算日志{}", supplierWalletLog);
            // 添加日志
            supplierWalletLogList.add(supplierWalletLog);
        }
        logger.info("供应商添加待结算金额完成，增加结算日志{}条", supplierWalletLogList.size());
        // 用mq批量保存钱包日志
        SendStatus sendWalletLogStatus = addSupplierWalletLogTemplate.syncSend(RocketMqConstant.ADD_SUPPLIER_WALLET_LOG_TOPIC, new GenericMessage<>(supplierWalletLogList)).getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountBySettledOrder(Long orderId) {

        // 商家之所以会从一笔订单拿到钱，是因为确认收货 or 订单退款时有部分不退
        long count = supplierWalletLogService.countInAmountByOrderId(orderId, SupplierWalletChangeReason.SETTLED.value());
        // 幂等
        if (count > 0) {
            return;
        }

        SupplierWalletLogSearchDTO supplierWalletLogSearchDTO = new SupplierWalletLogSearchDTO();
        supplierWalletLogSearchDTO.setOrderId(orderId);
        List<MongoSupplierWalletLogBO> supplierWalletLogs = supplierWalletLogService.listByParam(supplierWalletLogSearchDTO);
        if(CollectionUtil.isEmpty(supplierWalletLogs)){
            return;
        }
        // 商家需要结算的钱
        MongoSupplierWalletLogBO supplierPayLog = null;

        MongoSupplierWalletLogBO supplierRefundLog = new MongoSupplierWalletLogBO();
        supplierRefundLog.setUserAmount(0L);
        supplierRefundLog.setDistributionAmount(0L);
        supplierRefundLog.setPlatformAmount(0L);
        supplierRefundLog.setPlatformCommission(0L);
        supplierRefundLog.setChangeAmount(0L);
        for (MongoSupplierWalletLogBO supplierWalletLog : supplierWalletLogs) {
            // 下单
            MongoSupplierWalletLogBO payLog = polymerizationSupplierWalletLog(supplierRefundLog, supplierWalletLog);
            if (payLog != null) {
                supplierPayLog = payLog;
            }
        }
        // 非积分订单需要保存店铺结算记录
        if (Objects.nonNull(supplierPayLog)) {
            // 保存店铺结算记录
            saveSettledLog(supplierPayLog, supplierRefundLog);
        }
    }

    /**
     * 聚合订单支付退款导致商家金额变更的记录
     * @param refundWalletLog
     * @param supplierWalletLog
     * @return
     */
    private MongoSupplierWalletLogBO polymerizationSupplierWalletLog(MongoSupplierWalletLogBO refundWalletLog, MongoSupplierWalletLogBO supplierWalletLog) {
        if (Objects.equals(SupplierWalletChangeReason.PAY.value(), supplierWalletLog.getReason())) {
            return supplierWalletLog;
        }
        // 退款
        else if (Objects.equals(SupplierWalletChangeReason.ORDER_REFUND.value(), supplierWalletLog.getReason())) {
            refundWalletLog.setUserAmount(refundWalletLog.getUserAmount() + supplierWalletLog.getUserAmount());
            refundWalletLog.setDistributionAmount(0L);
            refundWalletLog.setPlatformAmount(0L);
            refundWalletLog.setPlatformCommission(refundWalletLog.getPlatformCommission() + supplierWalletLog.getPlatformCommission());
            // 如果因为退款收入，实际上是减少了退款的钱
            Long changeAmount = (supplierWalletLog.getIoType() == 1) ? -supplierWalletLog.getChangeAmount() : supplierWalletLog.getChangeAmount();
            refundWalletLog.setChangeAmount(refundWalletLog.getChangeAmount() + changeAmount);
        }
        return null;
    }


    private void saveSettledLog(MongoSupplierWalletLogBO supplierPayLog, MongoSupplierWalletLogBO supplierRefundLog) {
        Long changeAmount = supplierPayLog.getChangeAmount();
        MongoSupplierWalletLogBO supplierWalletLog = new MongoSupplierWalletLogBO();
        ServerResponseEntity<Long> response = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_SHOP_WALLET_LOG);
        if (!response.isSuccess()) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        supplierWalletLog.setWalletLogId(response.getData());
        supplierWalletLog.setOrderId(supplierPayLog.getOrderId());
        supplierWalletLog.setReason(SupplierWalletChangeReason.SETTLED.value());
        supplierWalletLog.setSupplierId(supplierPayLog.getSupplierId());
        supplierWalletLog.setCreateTime(new Date());
        supplierWalletLog.setUpdateTime(new Date());
        int ioType = supplierPayLog.getIoType();
        if(changeAmount - supplierRefundLog.getChangeAmount() < 0){
            ioType = 0;
        }
        // 如果之前未结算为负收入或者当前金额小于退款金额，则已结算的收入是负收入
        if(Objects.equals(ioType,0)){
            changeAmount = -changeAmount - supplierRefundLog.getChangeAmount();
        } else {
            changeAmount = changeAmount - supplierRefundLog.getChangeAmount();
        }
        supplierWalletLog.setIoType(ioType);
        supplierWalletLog.setAmountType(SupplierWalletAmountType.SETTLED_AMOUNT.value());
        supplierWalletLog.setChangeAmount(Math.abs(changeAmount));
        supplierWalletLog.setUserAmount(supplierPayLog.getUserAmount() - supplierRefundLog.getUserAmount());
        supplierWalletLog.setDistributionAmount(0L);
        supplierWalletLog.setPlatformAmount(0L);
        supplierWalletLog.setPlatformCommission(supplierPayLog.getPlatformCommission() - supplierRefundLog.getPlatformCommission());

        // 用mq保存钱包日志
        SendStatus sendWalletLogStatus = addSupplierWalletLogTemplate.syncSend(RocketMqConstant.ADD_SUPPLIER_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(supplierWalletLog))).getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }

        // 供应商结算，分成 1.减少待结算金额 和 2.增加结算金额以及总结算金额 两步操作，因为添加结算金额需要上锁操作，细粒化
        // 1.减少待结算金额，订单在哪个分片加的待结算金额就从哪里减掉
        boolean subUnsettledAmountSuccess = subUnsettledAmount(supplierWalletLog.getSupplierId(), supplierPayLog.getOrderId(), changeAmount);
        if (!subUnsettledAmountSuccess) {
            throw new LuckException("您的待结算金额不足，无法进行结算");
        }

        // 2.增加结算金额以及总结算金额，这里可以随机加在某个分片中
        RLock lock = redissonClient.getLock(LockCacheNames.LOCK_SUPPLIER_WALLET_ZONE_PREFIX + supplierWalletLog.getSupplierId());
        try {
            lock.lock();
            boolean addSettledAmountSuccess = addSettledAmount(supplierWalletLog.getSupplierId(), changeAmount);
            if (!addSettledAmountSuccess) {
                throw new LuckException("结算失败");
            }
        } finally {
            lock.unlock();
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundSupplierAmountByRefund(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {

        // 看看这个订单有没有锁定过
        long count = supplierWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), SupplierWalletChangeReason.ORDER_REFUND.value());
        // 幂等
        if (count > 0) {
            return;
        }
        // 供应商发货订单，未发货单项退款，且是最后一个订单的运费处理-- 赋值位置：com.jf.cloud.order.controller.supplier.OrderRefundController.agreeRefund
        if (Objects.nonNull(orderChangeShopWalletAmountBO.getSupplierFreightAmount()) && BooleanUtil.isTrue(orderChangeShopWalletAmountBO.isLastOrder())) {
            // 添加供应商发货的订单的运费，这个运费与商家和平台的运费处理规则不一致
            // 供应商发货的订单单项退款包含的运费为商品原本的运费，不受平台和商家运费减免金额的影响（商家不能修改供应商发货订单的运费，平台减免金额是给商家的，跟供应商没有关系）
            orderChangeShopWalletAmountBO.setFreightAmount(orderChangeShopWalletAmountBO.getSupplierFreightAmount());
        }
        // 供应商改变金额 = 供应商改变金额 + 供应商商品运费 - 平台供应商佣金改变量
        Long supplierRealRefundAmount = orderChangeShopWalletAmountBO.getChangePurchaseAmount() + orderChangeShopWalletAmountBO.getFreightAmount() - orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission();
        MongoSupplierWalletLogBO supplierWalletLog = new MongoSupplierWalletLogBO();
        supplierWalletLog.setWalletLogId(orderChangeShopWalletAmountBO.getWalletLogIds().remove(0));
        supplierWalletLog.setRefundId(orderChangeShopWalletAmountBO.getRefundId());
        supplierWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        supplierWalletLog.setReason(SupplierWalletChangeReason.ORDER_REFUND.value());
        supplierWalletLog.setSupplierId(orderChangeShopWalletAmountBO.getSupplierId());
        supplierWalletLog.setCreateTime(new Date());
        supplierWalletLog.setUpdateTime(new Date());
        supplierWalletLog.setFreightAmount(orderChangeShopWalletAmountBO.getFreightAmount());

        // 用户申请的退款金额
        supplierWalletLog.setUserAmount(orderChangeShopWalletAmountBO.getRefundAmount());
        // 回退的分销金额
        supplierWalletLog.setDistributionAmount(0L);
        // 回退的平台补贴
        supplierWalletLog.setPlatformAmount(0L);
        // 回退的平台佣金
        supplierWalletLog.setPlatformCommission(orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission());
        // 商家改变金额
        supplierWalletLog.setChangeAmount(supplierRealRefundAmount);
        // 扣除
        supplierWalletLog.setIoType(0);

        // 1. 订单未确认收货则扣未结算金额
        // 2. 商家撤销拼团活动时失效拼团时，将待成团的队伍进行退款

        // 减少未结算金额
        boolean isSuccess = subUnsettledAmount(orderChangeShopWalletAmountBO.getSupplierId(), orderChangeShopWalletAmountBO.getOrderId(), supplierRealRefundAmount);
        if (!isSuccess) {
            throw new LuckException("您的待结算金额不足，无法进行退款");
        }

        supplierWalletLog.setAmountType(SupplierWalletAmountType.UNSETTLED_AMOUNT.value());

        // 用mq保存钱包日志
        SendStatus sendWalletLogStatus = addSupplierWalletLogTemplate.syncSend(RocketMqConstant.ADD_SUPPLIER_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(supplierWalletLog))).getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
//
//        // 添加平台日志
//        supplierWalletLog.setSupplierId(Constant.PLATFORM_SHOP_ID);
//        // 平台实际上改变金额
//        // 订单退款，补贴肯定是减少了，但是对于平台来说是赚钱了
//        // 订单退款了，平台的佣金也是减少了，所以对于平台来说是亏钱的。
//        long platformRealRefundAmount = supplierWalletLog.getPlatformAmount() - supplierWalletLog.getPlatformCommission();
//        supplierWalletLog.setIoType(platformRealRefundAmount >= 0 ? 1 : 0);
//        supplierWalletLog.setChangeAmount(Math.abs(platformRealRefundAmount));
//        supplierWalletLogMapper.save(supplierWalletLog);
//
//        // 平台添加改变未结算金额
//        supplierWalletMapper.addUnsettledAmount(Constant.PLATFORM_SHOP_ID, platformRealRefundAmount);
//        // 通知分销，退回分销佣金
//        SendStatus sendStatus = refundSupplierNotifyDistributionTemplate.syncSend(RocketMqConstant.REFUND_SHOP_NOTIFY_DISTRIBUTION_TOPIC, new GenericMessage<>(orderChangeShopWalletAmountBO)).getSendStatus();
//        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
//            throw new LuckException(ResponseEnum.EXCEPTION);
//        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementSupplierAmountByRefund(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {

        // 看看这个订单有没有锁定过
        long count = supplierWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), SupplierWalletChangeReason.SETTLED.value());
        // 幂等
        if (count > 0) {
            return;
        }
        // 供应商收入，供应商收入 = 供应商剩余待结算金额 + 运费 - 已退金额 - 供应商剩余佣金
        Long supplierAmount = orderChangeShopWalletAmountBO.getChangePurchaseAmount()  + orderChangeShopWalletAmountBO.getFreightAmount() - orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission();
//        Long supplierAmount = orderChangeShopWalletAmountBO.getChangePurchaseAmount() - orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission();
        if(Objects.equals(supplierAmount ,0L) || Objects.equals(orderChangeShopWalletAmountBO.getSupplierId(),0L)) {
            return;
        }
        // 平台佣金改变量
        Long changePlatformCommission = orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission();
        MongoSupplierWalletLogBO supplierWalletLog = new MongoSupplierWalletLogBO();
        ServerResponseEntity<Long> response = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_SHOP_WALLET_LOG);
        if (!response.isSuccess()) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        supplierWalletLog.setWalletLogId(response.getData());
        supplierWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        supplierWalletLog.setReason(SupplierWalletChangeReason.SETTLED.value());
        supplierWalletLog.setSupplierId(orderChangeShopWalletAmountBO.getSupplierId());
        supplierWalletLog.setCreateTime(new Date());
        supplierWalletLog.setUpdateTime(new Date());

        // 结算的金额
        supplierWalletLog.setUserAmount(supplierAmount);
        // 回退的分销金额
        supplierWalletLog.setDistributionAmount(0L);
        // 回退的平台补贴
        supplierWalletLog.setPlatformAmount(0L);
        // 回退的平台佣金
        supplierWalletLog.setPlatformCommission(changePlatformCommission);
        // 商家改变金额
        supplierWalletLog.setChangeAmount(supplierAmount);
        // 增加
        supplierWalletLog.setIoType(1);
        // 1. 订单部分退款导致订单关闭 商家结算操作
        // 供应商结算操作同订单结算一样，分成两步
        //      1.减少待结算金额，订单在哪个分片加的待结算金额就从哪里减掉
        boolean subUnsettledAmountSuccess = subUnsettledAmount(supplierWalletLog.getSupplierId(), orderChangeShopWalletAmountBO.getOrderId(), supplierAmount);
        if (!subUnsettledAmountSuccess) {
            throw new LuckException("您的待结算金额不足，无法进行退款");
        }

        //      2.增加结算金额以及总结算金额，这里可以随机加在某个分片中
        RLock lock = redissonClient.getLock(LockCacheNames.LOCK_SUPPLIER_WALLET_ZONE_PREFIX + supplierWalletLog.getSupplierId());
        try {
            lock.lock();
            boolean addSettledAmountSuccess = addSettledAmount(supplierWalletLog.getSupplierId(), supplierAmount);
            if (!addSettledAmountSuccess) {
                throw new LuckException("供应商结算失败，无法进行退款");
            }
        } finally {
            lock.unlock();
        }

        supplierWalletLog.setAmountType(SupplierWalletAmountType.SETTLED_AMOUNT.value());
        // 添加日志
        // 用mq保存钱包日志
        SendStatus sendWalletLogStatus = addSupplierWalletLogTemplate.syncSend(RocketMqConstant.ADD_SUPPLIER_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(supplierWalletLog))).getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateByDistributionAmount(List<SupplierWalletBO> message) {
        if (CollUtil.isEmpty(message)) {
            return;
        }
        List<MongoSupplierWalletLogBO> supplierWalletLogList = new ArrayList<>();
        for (SupplierWalletBO supplierWalletBO : message) {
            Long supplierId = supplierWalletBO.getSupplierId();
            Long orderId = supplierWalletBO.getOrderId();
            // 商家待结算改变金额，从分销过来的待结算金额是个负数，用来减少待结算金额
            Long unsettledAmount = supplierWalletBO.getUnsettledAmount();
            // 幂等处理
            long count = supplierWalletLogService.countInAmountByOrderId(orderId, SupplierWalletChangeReason.DISTRIBUTION_AMOUNT.value());
            if (count > 0) {
                continue;
            }

            // 修改支付成功结算的店铺钱包日志
            SupplierWalletLogSearchDTO supplierWalletLogQuery = new SupplierWalletLogSearchDTO();
            supplierWalletLogQuery.setOrderId(orderId);
            supplierWalletLogQuery.setReason(SupplierWalletChangeReason.PAY.value());
            supplierWalletLogQuery.setSupplierId(supplierId);
            supplierWalletLogQuery.setIoType(1);
            supplierWalletLogQuery.setAmountType(SupplierWalletAmountType.UNSETTLED_AMOUNT.value());
            // 必须保存店铺分账完成，有该条分账记录后，才可以分销
            MongoSupplierWalletLogBO supplierWalletLogUpdate = supplierWalletLogService.listByParam(supplierWalletLogQuery).get(0);
            if (Objects.isNull(supplierWalletLogUpdate)) {
                throw new LuckException("未找到支付成功结算的店铺钱包日志");
            }
            supplierWalletLogUpdate.setDistributionAmount(Math.abs(unsettledAmount));

            // 修改钱包日志分销金额
            Query query = new Query();
            query.addCriteria(Criteria.where("_id").is(supplierWalletLogUpdate.getWalletLogId()));
            Update update = new Update();
            update.set("distributionAmount", supplierWalletLogUpdate.getDistributionAmount());
            update.set("updateTime", new Date());
            mongoTemplate.updateFirst(query, update, MongoSupplierWalletLogBO.class);

            // 更新钱包未结算金额
            addUnsettledAmount(supplierId, unsettledAmount, supplierWalletLogUpdate.getZoneNo());

            // 分销减少店铺钱包日志，只是为了添加一条日志，保证幂等
            MongoSupplierWalletLogBO supplierWalletLog = new MongoSupplierWalletLogBO();
            ServerResponseEntity<Long> response = segmentFeignClient.getSegmentId(DistributedIdKey.MALL4CLOUD_SHOP_WALLET_LOG);
            if (!response.isSuccess()) {
                throw new LuckException(ResponseEnum.EXCEPTION);
            }
            supplierWalletLog.setWalletLogId(response.getData());
            supplierWalletLog.setSupplierId(supplierId);
            supplierWalletLog.setOrderId(orderId);
            supplierWalletLog.setCreateTime(new Date());
            supplierWalletLog.setUpdateTime(new Date());
            supplierWalletLog.setIoType(unsettledAmount >= 0 ? 1 : 0);
            supplierWalletLog.setAmountType(SupplierWalletAmountType.UNSETTLED_AMOUNT.value());
            supplierWalletLog.setReason(SupplierWalletChangeReason.DISTRIBUTION_AMOUNT.value());
            supplierWalletLog.setDistributionAmount(Math.abs(unsettledAmount));
            supplierWalletLog.setChangeAmount(Math.abs(unsettledAmount));
            supplierWalletLog.setUserAmount(supplierWalletLogUpdate.getUserAmount());
            supplierWalletLogList.add(supplierWalletLog);
        }
        // 用mq批量保存钱包日志
        SendStatus sendWalletLogStatus = addSupplierWalletLogTemplate.syncSend(RocketMqConstant.ADD_SUPPLIER_WALLET_LOG_TOPIC, new GenericMessage<>(supplierWalletLogList)).getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    public PageVO<SupplierWalletVO> pageSupplierWalletByTime(PageDTO pageDTO, SupplierWalletLogSearchDTO supplierWalletLogSearchDTO) {
        PageVO<SupplierWalletVO> supplierWalletVOPage;
        if (Objects.nonNull(supplierWalletLogSearchDTO.getStartTime()) && Objects.nonNull(supplierWalletLogSearchDTO.getEndTime())) {
            supplierWalletVOPage = supplierWalletLogService.pageSupplierWalletByTime(pageDTO, supplierWalletLogSearchDTO);
        } else {
            supplierWalletVOPage = PageUtil.doPage(pageDTO, () -> supplierWalletMapper.listAllSupplierWallet(supplierWalletLogSearchDTO));
        }
        return supplierWalletVOPage;
    }

    @Override
    public SupplierWalletVO getBySupplierId(Long supplierId) {
        SupplierWalletVO supplierWallet;
        SupplierWalletZoneInfoVO supplierWalletZoneInfo = supplierWalletZoneService.getShopZoneInfo(supplierId);
        if (!Objects.isNull(supplierWalletZoneInfo) && Objects.equals(supplierWalletZoneInfo.getZoneNum(), SupplierWalletZoneConstant.ZERO)) {
            // 如果没有分片就按原来方式获取供应商钱包信息
            supplierWallet = supplierWalletMapper.getBySupplierId(supplierId);
        } else {
            // 从分片中获取供应商钱包信息
            supplierWallet = supplierWalletZoneMapper.getSupplierWalletByZone(supplierId);
        }
        return supplierWallet;
    }

    @Override
    public SupplierWalletVO getAllSupplier(SupplierWalletLogSearchDTO supplierWalletLogSearchDTO) {
        SupplierWalletVO supplierWallet;
        if (Objects.nonNull(supplierWalletLogSearchDTO.getStartTime()) && Objects.nonNull(supplierWalletLogSearchDTO.getEndTime())) {
            supplierWallet = supplierWalletLogService.getSupplierWalletByTime(supplierWalletLogSearchDTO);
        } else {
            supplierWallet = supplierWalletMapper.getAllSupplier(supplierWalletLogSearchDTO);
        }
        return supplierWallet;
    }

    /**
     * 下单添加待结算金额/分销减少待结算金额
     * 返回分片的序号，之后退款或者分销结算或者订单结算要扣待结算金额时根据分片序号和供应商id来扣减
     * 如果没有分片的话返回null，判断如果是空或就从主分片扣减
     */
    private Integer addUnsettledAmount(Long supplierId, Long changeAmount, Integer zoneNo) {
        SupplierWalletZoneInfoVO supplierWalletZoneInfo = supplierWalletZoneService.getShopZoneInfo(supplierId);
        if (Objects.equals(supplierWalletZoneInfo.getZoneNum(), SupplierWalletZoneConstant.ZERO)) {
            // 未分片就直接按原来的方式增加(因为添加了唯一索引，所以是不用怕多次加钱)
            supplierWalletMapper.addUnsettledAmount(supplierId, changeAmount);
            return null;
        } else {
            // 通过分片增加待结算金额
            if (Objects.isNull(zoneNo)) {
                Random random = new Random();
                zoneNo = random.nextInt(supplierWalletZoneInfo.getZoneNum());
            }
            // 如果是支付增加的待结算金额，这里的待结算金额不会为负
            // 如果是分销进来的这个方法，会根据这笔订单增加的待结算金额的钱包的zoneNo去进行扣减金额，结果也不会是负的
            supplierWalletZoneMapper.addUnsettledAmount(supplierId, changeAmount, zoneNo);
            return zoneNo;
        }
    }

    /**
     * 退款减少待结算金额
     */
    private boolean subUnsettledAmount(Long supplierId, Long orderId, Long changeAmount) {
        if (Objects.equals(changeAmount, 0L)) {
            return true;
        }
        SupplierWalletZoneInfoVO supplierWalletZoneInfo = supplierWalletZoneService.getShopZoneInfo(supplierId);
        if (Objects.equals(supplierWalletZoneInfo.getZoneNum(), SupplierWalletZoneConstant.ZERO)) {
            // 未分片就直接按原来的方式扣减待结算金额
            return supplierWalletMapper.subUnsettledAmount(supplierId, changeAmount) > 0;
        } else {
            SupplierWalletLogSearchDTO shopWalletLogQuery = new SupplierWalletLogSearchDTO();
            shopWalletLogQuery.setOrderId(orderId);
            shopWalletLogQuery.setReason(SupplierWalletChangeReason.PAY.value());
            shopWalletLogQuery.setSupplierId(supplierId);
            shopWalletLogQuery.setAmountType(SupplierWalletAmountType.UNSETTLED_AMOUNT.value());
            // 找到支付时的钱包日志记录
            MongoSupplierWalletLogBO shopWalletLog = supplierWalletLogService.listByParam(shopWalletLogQuery).get(0);
            if (Objects.isNull(shopWalletLog)) {
                throw new LuckException("未找到支付成功结算的供应商钱包日志");
            }

            Integer zoneNo = shopWalletLog.getZoneNo();
            if (Objects.isNull(zoneNo)) {
                // 如果是空的，可能是以前的订单数据，从主分片中扣
                return supplierWalletMapper.subUnsettledAmount(supplierId, changeAmount) > 0;
            }
            return supplierWalletZoneMapper.subUnsettledAmount(supplierId, changeAmount, zoneNo) > 0;
        }
    }

    /**
     * 增加结算金额
     */
    private boolean addSettledAmount(Long shopId, Long changeAmount) {
        if (Objects.equals(changeAmount, 0L)) {
            return true;
        }
        SupplierWalletZoneInfoVO supplierWalletZoneInfo = supplierWalletZoneService.getShopZoneInfo(shopId);

        if (Objects.equals(supplierWalletZoneInfo.getZoneNum(), SupplierWalletZoneConstant.ZERO)) {
            // 没有分片就按原来的方式增加
            return supplierWalletMapper.addSettledAmount(shopId, changeAmount) > 0;
        } else {
            Random random = new Random();
            int zoneNo = random.nextInt(supplierWalletZoneInfo.getZoneNum());
            // 通过分片增加
            return supplierWalletZoneMapper.addSettledAmount(shopId, changeAmount, zoneNo) > 0;
        }
    }
}
